package com.log4jviewer.logfile.fields;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.log4jviewer.logfile.SpecialCharsConverter;

/**
 * Class constructs Regex for log's date field.
 * 
 * @author Apache
 */
public class DateField extends AbstractField {

    private String dateFormatString;

    private DateFormat dateFormat;

    private StringBuilder rawRegex;

    public DateField(final LogFieldName logFieldName, final boolean leftAlign, final boolean rightAlign,
            final String dateFormatString) {
        super(logFieldName, leftAlign, rightAlign);
        rawRegex = new StringBuilder();
        this.dateFormatString = dateFormatString;
        this.dateFormat = new SimpleDateFormat(dateFormatString);
        compile();
    }

    @Override
    public Object decodeValue(final String value) throws ParseException {
        Date date = dateFormat.parse(value);
        Object decodeValue = dateFormat.format(date);
        return decodeValue;
    }

    @Override
    public String getRawRegex() {
        return rawRegex.toString();
    }

    private void compile() {

        StringBuilder rawRegexBuilder = new StringBuilder();
        int index = 0;

        while (index < dateFormatString.length()) {
            char currentCharInDateFormat = dateFormatString.charAt(index);

            if (currentCharInDateFormat == '\'') {
                appendCharsToRawRegex(rawRegexBuilder);

                // extract literal
                int charIndex = dateFormatString.indexOf('\'', index + 1);
                String dateFormatPart = dateFormatString.substring(index + 1, charIndex);
                index = charIndex;

                if (dateFormatPart.length() > 0) {
                    rawRegex.append(SpecialCharsConverter.convertSpecialChars(dateFormatPart));
                } else {
                    rawRegex.append('\'');
                }
            } else {
                switch (currentCharInDateFormat) {
                case 'G':
                case 'y':
                case 'M':
                case 'w':
                case 'W':
                case 'D':
                case 'd':
                case 'F':
                case 'E':
                case 'a':
                case 'H':
                case 'k':
                case 'K':
                case 'h':
                case 'm':
                case 'S':
                case 's':
                case 'z':
                case 'Z':
                    appendCharsToRawRegex(rawRegexBuilder);
                    StringBuilder dateFormatChars = new StringBuilder();

                    while (index < dateFormatString.length()) {
                        char currentChar = dateFormatString.charAt(index);

                        if (currentChar != currentCharInDateFormat) {
                            index--;
                            break;
                        }
                        dateFormatChars.append(currentChar);
                        index++;
                    }
                    finalizePattern(currentCharInDateFormat, dateFormatChars.toString());
                    break;

                default:
                    rawRegexBuilder.append(currentCharInDateFormat);
                }
            }
            index++;
        }

        appendCharsToRawRegex(rawRegexBuilder);
    }

    private void appendCharsToRawRegex(final StringBuilder chars) {
        if (chars.length() > 0) {
            rawRegex.append(SpecialCharsConverter.convertSpecialChars(chars.toString()));
            chars.setLength(0);
        }
    }

    private void finalizePattern(final char currentCharInDateFormat, final String dateFormatChars) {

        int dateFormatCharsLength = dateFormatChars.length();

        switch (currentCharInDateFormat) {
        case 'G':
            rawRegex.append("(?:BC|AD)");
            break;

        case 'y':
            if ((dateFormatCharsLength > 0) && (dateFormatCharsLength < 4)) {
                rawRegex.append("\\d{2}");
            } else {
                rawRegex.append("\\d{").append(dateFormatCharsLength).append("}");
            }
            break;

        case 'M':
            if (dateFormatCharsLength == 1) {
                rawRegex.append("\\d{1,2}");
            } else if (dateFormatCharsLength == 2) {
                rawRegex.append("\\d{2}");
            } else if (dateFormatCharsLength == 3) {
                rawRegex.append("(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Agu|Sep|Oct|Nov|Dec)");
            } else {
                rawRegex.append("(?:January|February|March|April|May|June|July|Aguest|"
                        + "September|October|November|December)");
            }
            break;

        case 'E':
            if ((dateFormatCharsLength >= 0) && (dateFormatCharsLength < 4)) {
                rawRegex.append("(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)");
            } else {
                rawRegex.append("(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)");
            }
            break;

        case 'a':
            rawRegex.append("(?:AM|PM)");
            break;

        case 'W':
        case 'F':
            rawRegex.append("\\d{").append(dateFormatCharsLength).append("}");
            break;

        case 'w':
        case 'd':
        case 'H':
        case 'k':
        case 'K':
        case 'h':
        case 'm':
        case 's':
            if (dateFormatCharsLength == 1) {
                rawRegex.append("\\d{1,2}");
            } else {
                rawRegex.append("\\d{").append(dateFormatCharsLength).append("}");
            }
            break;

        case 'D':
        case 'S':
            if (dateFormatCharsLength == 1) {
                rawRegex.append("\\d{1,3}");
            } else if (dateFormatCharsLength == 2) {
                rawRegex.append("\\d{2,3}");
            } else {
                rawRegex.append("\\d{").append(dateFormatCharsLength).append("}");
            }
            break;

        case 'z':
            if ((dateFormatCharsLength > 0) && (dateFormatCharsLength < 4)) {
                rawRegex.append("[A-Z]{2}T");
            } else {
                rawRegex.append("[a-zA-Z]+ Time");
            }
            break;

        case 'Z':
            rawRegex.append("[\\+\\-]\\d{4}");
            break;

        default:
            // no code
        }
    }
}
